/*
This code is based on source provided under the terms of the Id Software 
LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the
GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of 
LICENSE_ID, please contact Id Software immediately at info@idsoftware.com.

All changes and additions to the original source which have been developed by
other contributors (see CONTRIBUTORS) are provided under the terms of the
license the contributors choose (see LICENSE), to the extent permitted by the
LICENSE_ID. If you did not receive a copy of the contributor license,
please contact the GtkRadiant maintainers at info@gtkradiant.com immediately.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//====================================================================
//
// Name:			l_net.c
// Function:		-
// Programmer:		MrElusive
// Last update:		-
// Tab size:		3
// Notes:
//====================================================================

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include "l_net.h"
#include "l_net_wins.h"

#define GetMemory malloc
#define FreeMemory free

#define qtrue	1
#define qfalse	0

#ifdef _DEBUG
void WinPrint(char *str, ...)
{
	va_list argptr;
  char text[4096];

  va_start (argptr,str);
	vsprintf (text, str, argptr);
	va_end (argptr);

  printf(text);
}
#else
void WinPrint(char *str, ...)
{
}
#endif

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Net_SetAddressPort(address_t *address, int port)
{
	sockaddr_t addr;

	WINS_StringToAddr(address->ip, &addr);
	WINS_SetSocketPort(&addr, port);
	strcpy(address->ip, WINS_AddrToString(&addr));
} //end of the function Net_SetAddressPort
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int Net_AddressCompare(address_t *addr1, address_t *addr2)
{
#ifdef WIN32
	return stricmp(addr1->ip, addr2->ip);
#endif
#ifdef __linux__
	return strcasecmp(addr1->ip, addr2->ip);
#endif
} //end of the function Net_AddressCompare
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Net_SocketToAddress(socket_t *sock, address_t *address)
{
	strcpy(address->ip, WINS_AddrToString(&sock->addr));
} //end of the function Net_SocketToAddress
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int Net_Send(socket_t *sock, netmessage_t *msg)
{
	int size;

	size = msg->size;
	msg->size = 0;
	NMSG_WriteLong(msg, size-4);
	msg->size = size;
	//WinPrint("Net_Send: message of size %d\n", sendmsg.size);
	return WINS_Write(sock->socket, msg->data, msg->size, NULL);
} //end of the function Net_SendSocketReliable
//===========================================================================
// returns the number of bytes recieved
// -1 on error
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int Net_Receive(socket_t *sock, netmessage_t *msg)
{
	int curread;

	if (sock->remaining > 0)
	{
		curread = WINS_Read(sock->socket, &sock->msg.data[sock->msg.size], sock->remaining, NULL);
		if (curread == -1)
		{
			WinPrint("Net_Receive: read error\n");
			return -1;
		} //end if
		sock->remaining -= curread;
		sock->msg.size += curread;
		if (sock->remaining <= 0)
		{
			sock->remaining = 0;
			memcpy(msg, &sock->msg, sizeof(netmessage_t));
			sock->msg.size = 0;
			return msg->size - 4;
		} //end if
		return 0;
	} //end if
	sock->msg.size = WINS_Read(sock->socket, sock->msg.data, 4, NULL);
	if (sock->msg.size == 0) return 0;
	if (sock->msg.size == -1)
	{
		WinPrint("Net_Receive: size header read error\n");
		return -1;
	} //end if
	//WinPrint("Net_Receive: message size header %d\n", msg->size);
	sock->msg.read = 0;
	sock->remaining = NMSG_ReadLong(&sock->msg);
	if (sock->remaining == 0) return 0;
	if (sock->remaining < 0 || sock->remaining > MAX_NETMESSAGE)
	{
		WinPrint("Net_Receive: invalid message size %d\n", sock->remaining);
		return -1;
	} //end if
	//try to read the message
	curread = WINS_Read(sock->socket, &sock->msg.data[sock->msg.size], sock->remaining, NULL);
	if (curread == -1)
	{
		WinPrint("Net_Receive: read error\n");
		return -1;
	} //end if
	sock->remaining -= curread;
	sock->msg.size += curread;
	if (sock->remaining <= 0)
	{
		sock->remaining = 0;
		memcpy(msg, &sock->msg, sizeof(netmessage_t));
		sock->msg.size = 0;
		return msg->size - 4;
	} //end if
	//the message has not been completely read yet
#ifdef _DEBUG
  printf("++timo TODO: debug the Net_Receive on big size messages\n");
#endif
	return 0;
} //end of the function Net_Receive
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
socket_t *Net_AllocSocket(void)
{
	socket_t *sock;

	sock = (socket_t *) GetMemory(sizeof(socket_t));
	memset(sock, 0, sizeof(socket_t));
	return sock;
} //end of the function Net_AllocSocket
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Net_FreeSocket(socket_t *sock)
{
	FreeMemory(sock);
} //end of the function Net_FreeSocket
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
socket_t *Net_Connect(address_t *address, int port)
{
	int newsock;
	socket_t *sock;
	sockaddr_t sendaddr;

	// see if we can resolve the host name
	WINS_StringToAddr(address->ip, &sendaddr);

  newsock = WINS_OpenReliableSocket(port);
	if (newsock == -1) return NULL;

	sock = Net_AllocSocket();
	if (sock == NULL)
	{
		WINS_CloseSocket(newsock);
		return NULL;
	} //end if
	sock->socket = newsock;

	//connect to the host
	if (WINS_Connect(newsock, &sendaddr) == -1)
	{
		Net_FreeSocket(sock);
		WINS_CloseSocket(newsock);
		WinPrint("Net_Connect: error connecting\n");
		return NULL;
	} //end if

	memcpy(&sock->addr, &sendaddr, sizeof(sockaddr_t));
	//now we can send messages
	//
	return sock;
} //end of the function Net_Connect

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
socket_t *Net_ListenSocket(int port)
{
	int newsock;
	socket_t *sock;

	newsock = WINS_OpenReliableSocket(port);
	if (newsock == -1) return NULL;

	if (WINS_Listen(newsock) == -1)
	{
		WINS_CloseSocket(newsock);
		return NULL;
	} //end if
	sock = Net_AllocSocket();
	if (sock == NULL)
	{
		WINS_CloseSocket(newsock);
		return NULL;
	} //end if
	sock->socket = newsock;
	WINS_GetSocketAddr(newsock, &sock->addr);
	WinPrint("listen socket opened at %s\n", WINS_AddrToString(&sock->addr));
	//
	return sock;
} //end of the function Net_ListenSocket
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
socket_t *Net_Accept(socket_t *sock)
{
	int newsocket;
	sockaddr_t sendaddr;
	socket_t *newsock;

	newsocket = WINS_Accept(sock->socket, &sendaddr);
	if (newsocket == -1) return NULL;

	newsock = Net_AllocSocket();
	if (newsock == NULL)
	{
		WINS_CloseSocket(newsocket);
		return NULL;
	} //end if
	newsock->socket = newsocket;
	memcpy(&newsock->addr, &sendaddr, sizeof(sockaddr_t));
	//
	return newsock;
} //end of the function Net_Accept
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Net_Disconnect(socket_t *sock)
{
	WINS_CloseSocket(sock->socket);
	Net_FreeSocket(sock);
} //end of the function Net_Disconnect
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Net_StringToAddress(char *string, address_t *address)
{
	strcpy(address->ip, string);
} //end of the function Net_StringToAddress
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Net_MyAddress(address_t *address)
{
	strcpy(address->ip, WINS_MyAddress());
} //end of the function Net_MyAddress
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int Net_Setup(void)
{
	WINS_Init();
	//
	WinPrint("my address is %s\n", WINS_MyAddress());
	//
	return qtrue;
} //end of the function Net_Setup
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Net_Shutdown(void)
{
	WINS_Shutdown();
} //end of the function Net_Shutdown
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void NMSG_Clear(netmessage_t *msg)
{
	msg->size = 4;
} //end of the function NMSG_Clear
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void NMSG_WriteChar (netmessage_t *msg, int c)
{
	if (c < -128 || c > 127)
		WinPrint("NMSG_WriteChar: range error\n");

	if (msg->size >= MAX_NETMESSAGE)
	{
		WinPrint("NMSG_WriteChar: overflow\n");
		return;
	} //end if
	msg->data[msg->size] = c;
	msg->size++;
} //end of the function NMSG_WriteChar
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void NMSG_WriteByte(netmessage_t *msg, int c)
{
	if (c < -128 || c > 127)
		WinPrint("NMSG_WriteByte: range error\n");

	if (msg->size + 1 >= MAX_NETMESSAGE)
	{
		WinPrint("NMSG_WriteByte: overflow\n");
		return;
	} //end if
	msg->data[msg->size] = c;
	msg->size++;
} //end of the function NMSG_WriteByte
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void NMSG_WriteShort(netmessage_t *msg, int c)
{
	if (c < ((short)0x8000) || c > (short)0x7fff)
		WinPrint("NMSG_WriteShort: range error");

	if (msg->size + 2 >= MAX_NETMESSAGE)
	{
		WinPrint("NMSG_WriteShort: overflow\n");
		return;
	} //end if
	msg->data[msg->size] = c&0xff;
	msg->data[msg->size+1] = c>>8;
	msg->size += 2;
} //end of the function NMSG_WriteShort
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void NMSG_WriteLong(netmessage_t *msg, int c)
{
	if (msg->size + 4 >= MAX_NETMESSAGE)
	{
		WinPrint("NMSG_WriteLong: overflow\n");
		return;
	} //end if
	msg->data[msg->size] = c&0xff;
	msg->data[msg->size+1] = (c>>8)&0xff;
	msg->data[msg->size+2] = (c>>16)&0xff;
	msg->data[msg->size+3] = c>>24;
	msg->size += 4;
} //end of the function NMSG_WriteLong
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void NMSG_WriteFloat(netmessage_t *msg, float c)
{
	if (msg->size + 4 >= MAX_NETMESSAGE)
	{
		WinPrint("NMSG_WriteLong: overflow\n");
		return;
	} //end if
	msg->data[msg->size] = *((int *)&c)&0xff;
	msg->data[msg->size+1] = (*((int *)&c)>>8)&0xff;
	msg->data[msg->size+2] = (*((int *)&c)>>16)&0xff;
	msg->data[msg->size+3] = *((int *)&c)>>24;
	msg->size += 4;
} //end of the function NMSG_WriteFloat
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void NMSG_WriteString(netmessage_t *msg, char *string)
{
	if (msg->size + strlen(string) + 1 >= MAX_NETMESSAGE)
	{
		WinPrint("NMSG_WriteString: overflow\n");
		return;
	} //end if
	strcpy(&msg->data[msg->size], string);
	msg->size += strlen(string) + 1;
} //end of the function NMSG_WriteString
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void NMSG_ReadStart(netmessage_t *msg)
{
	msg->readoverflow = qfalse;
	msg->read = 4;
} //end of the function NMSG_ReadStart
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int NMSG_ReadChar(netmessage_t *msg)
{
	if (msg->size + 1 > msg->size)
	{
		msg->readoverflow = qtrue;
		WinPrint("NMSG_ReadChar: read overflow\n");
		return 0;
	} //end if
	msg->read++;
	return msg->data[msg->read-1];
} //end of the function NMSG_ReadChar
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int NMSG_ReadByte(netmessage_t *msg)
{
	if (msg->read + 1 > msg->size)
	{
		msg->readoverflow = qtrue;
		WinPrint("NMSG_ReadByte: read overflow\n");
		return 0;
	} //end if
	msg->read++;
	return msg->data[msg->read-1];
} //end of the function NMSG_ReadByte
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int NMSG_ReadShort(netmessage_t *msg)
{
	int c;

	if (msg->read + 2 > msg->size)
	{
		msg->readoverflow = qtrue;
		WinPrint("NMSG_ReadShort: read overflow\n");
		return 0;
	} //end if
	c = (short)(msg->data[msg->read] + (msg->data[msg->read+1]<<8));
	msg->read += 2;
	return c;
} //end of the function NMSG_ReadShort
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int NMSG_ReadLong(netmessage_t *msg)
{
	int c;

	if (msg->read + 4 > msg->size)
	{
		msg->readoverflow = qtrue;
		WinPrint("NMSG_ReadLong: read overflow\n");
		return 0;
	} //end if
	c = msg->data[msg->read]
		+ (msg->data[msg->read+1]<<8)
		+ (msg->data[msg->read+2]<<16)
		+ (msg->data[msg->read+3]<<24);
	msg->read += 4;
	return c;
} //end of the function NMSG_ReadLong
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
float NMSG_ReadFloat(netmessage_t *msg)
{
	int c;

	if (msg->read + 4 > msg->size)
	{
		msg->readoverflow = qtrue;
		WinPrint("NMSG_ReadLong: read overflow\n");
		return 0;
	} //end if
	c = msg->data[msg->read]
		+ (msg->data[msg->read+1]<<8)
		+ (msg->data[msg->read+2]<<16)
		+ (msg->data[msg->read+3]<<24);
	msg->read += 4;
	return *(float *)&c;
} //end of the function NMSG_ReadFloat
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
char *NMSG_ReadString(netmessage_t *msg)
{
	static char	string[2048];
	int l, c;
	
	l = 0;
	do
	{
		if (msg->read + 1 > msg->size)
		{
			msg->readoverflow = qtrue;
			WinPrint("NMSG_ReadString: read overflow\n");
			string[l] = 0;
			return string;
		} //end if
		c = msg->data[msg->read];
		msg->read++;
		if (c == 0) break;
		string[l] = c;
		l++;
	} while (l < sizeof(string)-1);
	string[l] = 0;
	return string;
} //end of the function NMSG_ReadString
